#Sean Breid POS Tagger.  The problem at hand is a part of speech tagger. With 
#this program we want to take a file with filled with text and give each word
#in it the part of speech that corresponds to that word eg noun, verb and so on.
#We plan on doing this by going through a set of data that will train our
#program. We will find for each word its most probable tag based on the training
#data for example if we see the word "the" and its most common tag is "DT" than
#when we see the word "the" in the text to be tagged, we will tag it as a "DT"
#The input for this program should follow like this "perl tagger <training-data
#-file> <file-to-be-tagged> > <file-of-tagged-output>" where the file-of-
#tagged-output is a filename for a file that the program will make and it will
#have the tags that the tagger predicts. The algorithm for the model starts off
#by formatting the lines of the first file and then parsing the tags.  It
#ignores the braces then parses the word from the part of speech with a regular
#expression. Then it makes a hash of a hash.  The key of the first hash is the
#word and the key of the hash that is returned by the first hash is the tag. The
#value of this second hash is the number of times the word, which is the key of
#the first hash, is seen with the tag, which is the key of the second hash.
#After is has done this it now tags the parts of speech of the test file.  It
#goes through the file and grabs each word from the test file.  With each word
#it find the most likely tag with the hash of hash table.After doing this it
#certain rules are applied to try to correct common errors in the tagging. The
#Each rule has it own subroutine and the details of these rules can be found in
#the documentation of these subroutines.  Then it prints the current word 
#followed by a slash which is then followed by the chosen tag.

initialize();


while(<>){

    my @current_line = format_line($_);#formats the line

    parse_tags(\@current_line);#parses the training data file

    last if eof;#breaks when reached the end of the traning data file


}

 #while (($key, $value) = each(%pos_hash)){
    #print $key."~> ".$value."\n";
#   while(($key2, $value2) = each %$value){
#	print $key."~> ".$key2."~> ".$value2."\n";

#   }
#}


while(<>){
    
    my @current_line = format_line($_);

    tag_line(\@current_line);

    #print "@current_line";

}

 


################################################################################
#initializes all of the data structures for the program

sub initialize(){

    %pos_hash = {};

    $previous_tag = "";

}

################################################################################
#formats the line for into an array of tokens
#param 1: the string of the line to be formatted

sub format_line{

    chomp;#removes newline characters

    s/(\[|\])/ $1 /g;#puts spaces between punctutation

    my @line = split;#splits up all of the tokens

    return @line;

}


################################################################################
#parses the tokens into words and their respective tags and puts their
#occurances in a hash
#param 1: a reference to an array of tokens to be parsed

sub parse_tags{

    my @my_line = @{ $_[0] };#convert the reference to the array into an array

    foreach $word(@my_line){#for every token in this line

	if($word =~ /(.*)\/(.*)/){#split the word from the tag

	    #print "word: ".$1." tag: ".$2."\n";

	    if($pos_hash{$1}{$2}){
		#if hash entry already exists add one occurance
		$pos_hash{$1}{$2} += 1;

	    }else{#hash entry does not exist already

		$pos_hash{$1}{$2} = 1;

	    }

	}

    }

}





################################################################################
#parses through a file and does part of speech tagging on that line
#param 1: the line which is to be tagged

sub tag_line{

    my @my_line = @{ $_[0] };#convert the reference to the array into an array
    
    foreach $word(@my_line){#for every token in this line

	if(brace_check($word)){#checks if $word is a brace

	    print $word." ";

	}
	else{#if $word is not a brace

	    $tag = choose_tag($word);

	    print $word."/".$tag." ";

	}

    }
}


################################################################################
#checks if this word is a brace
#param 1: a token to check

sub brace_check{

    my $my_word = $_[0];
    
    if($word =~ /[\[\]]/){

	return 1;

    }
    else{

	return 0;

    }

}


################################################################################
#chooses the tag for the given word
#param 1: the given word
#returns the tag

sub choose_tag{

    my $my_word = $_[0];

    my %my_word_hash =  %{$pos_hash{$my_word}};#hash my_word from pos_hash
    #for the hash of occurances

    my $highest_occurance = 0;#number of occurances of the most frequent tag
    #for $my_word

    my $chosen_tag = "";

    while (($key, $value) = each(%my_word_hash)){
	#print $key."~> ".$value."\n";

	if($value > $highest_occurance){
	    #if our current $value is greater than the most frequent tag so far
	    $highest_occurance = $value;
	    $chosen_tag = $key;

	}

    }

    if($highest_occurance == 0){

	$chosen_tag = "NN";

    }

    $chosen_tag = rule_one($chosen_tag);

    $chosen_tag = rule_two($my_word, $chosen_tag);

    $chosen_tag = rule_three($my_word, $chosen_tag);

    $chosen_tag = rule_four($my_word, $chosen_tag);

    $chosen_tag = rule_five($my_word, $chosen_tag);

    $chosen_tag = rule_six($my_word, $chosen_tag);

    $previous_tag = $chosen_tag;

    return $chosen_tag;

}


################################################################################
#this is rule 1: which will turn any VB following a DT into an NN

sub rule_one{

    $current_tag = $_[0];

    if($previous_tag eq "DT"){


	if($current_tag eq "VB"){

	    return "NN";

	}
	else{

	    return $current_tag;

	}

    }
    else{

	return $current_tag;

    }

}


################################################################################
#the second rule takes any token with a number in it and turns it into a CD
#param 1: the token to be checked


sub rule_two{

    my $this_word = $_[0];

    my $current_tag = $_[1];

    if($this_word =~ /[0-9]/){
	
	return "CD";

    }
    else{

	return $current_tag;

    }

}

################################################################################
#the third rule takes any word that is not at the beginning of a sentence and
#has a capital letter and turns it into a NNP
#param 1: the token to be checked
#param 2: the current pos

sub rule_three{

    my $this_word = $_[0];

    my $current_tag = $_[1];

    if($previous_tag ne "."){

	if($this_word =~ /^[A-Z](.*)/){

	    #print STDERR $this_word.": ".$current_tag."\n";

	    return "NNP";

	}
	else{

	    return $current_tag;

	}

    }
    else{

	return $current_tag;

    }

}

################################################################################
#if the word is "the" or "a" make it a determiner

sub rule_four{

    my $this_word = $_[0];

    my $current_tag = $_[1];

    if($this_word =~ /\bThe\b/){

	#print STDERR $this_word."\n";
	return "DT";

    }
    else{

	return $current_tag;

    }


}


################################################################################
#if the word has a '-' turn it into a JJ
#param 1: the current word
#param 2: the current tag

sub rule_five{

    my $this_word = $_[0];

    my $current_tag = $_[1];

     if($this_word =~ /-/){

	#print STDERR $this_word."\n";
	return "JJ";

    }
    else{

	return $current_tag;

    }

}

################################################################################
#if the word is a NN and has an s at the end turn it into an NNS
#param 1: the current token
#param 2: the current tag


sub rule_six{

    my $this_word = $_[0];

    my $current_tag = $_[1];

    if($this_word =~ /s$/ && "NN" eq $current_tag){

	#print STDERR $this_word."\n";
	return "NNS";

    }
    else{

	return $current_tag;

    }



}
